package com.spaceobj.chess.handler;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.spaceobj.chess.common.Message;
import com.spaceobj.chess.common.MessageType;
import com.spaceobj.chess.entity.GameChatInfo;
import com.spaceobj.chess.entity.GameState;
import com.spaceobj.chess.entity.MatchQueueItem;
import com.spaceobj.chess.entity.Move;
import com.spaceobj.chess.entity.Spectator;
import com.spaceobj.chess.entity.UserSession;
import com.spaceobj.chess.service.MySQLService;
import com.spaceobj.chess.service.RedisService;
import com.spaceobj.chess.validator.MoveValidator;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
public class GameServerHandler extends ChannelInboundHandlerAdapter {

    @Autowired
    private RedisService redisService;

    @Autowired
    private MySQLService mysqlService;

    @Autowired
    private MoveValidator moveValidator;

    private final Map<String, Room> rooms = new ConcurrentHashMap<>();

    private static class Room {
        // 房间ID
        private String roomId;
        // 玩家映射表,key为用户ID,value为对应的通信Channel
        private Map<Integer, Channel> players;
        // 观战者映射表,key为用户ID,value为对应的通信Channel
        private Map<Integer, Channel> spectators;
        // 游戏状态
        private GameState gameState;
        // 房间创建时间
        private LocalDateTime createTime;
        // 房间最后活跃时间
        private LocalDateTime lastActiveTime;

        public Room(String roomId) {
            this.roomId = roomId;
            this.players = new HashMap<>();
            this.spectators = new HashMap<>();
            this.createTime = LocalDateTime.now();
            this.lastActiveTime = LocalDateTime.now();
        }

        public Map<Integer, Channel> getPlayers() {
            return players;
        }

        public Map<Integer, Channel> getSpectators() {
            return spectators;
        }

        public List<Channel> getRoomChannels() {
            List<Channel> channels = new ArrayList<>();
            channels.addAll(players.values());
            channels.addAll(spectators.values());
            return channels;
        }

        public void addPlayer(Integer userId, Channel channel) {
            players.put(userId, channel);
            lastActiveTime = LocalDateTime.now();
        }

        public void removePlayer(Integer userId) {
            players.remove(userId);
            lastActiveTime = LocalDateTime.now();
        }

        public void addSpectator(Integer userId, Channel channel) {
            spectators.put(userId, channel);
            lastActiveTime = LocalDateTime.now();
        }

        public void removeSpectator(Integer userId) {
            spectators.remove(userId);
            lastActiveTime = LocalDateTime.now();
        }

        public boolean isPlayer(Integer userId) {
            return players.containsKey(userId);
        }

        public boolean isSpectator(Integer userId) {
            return spectators.containsKey(userId);
        }

        public Channel getPlayerChannel(Integer userId) {
            return players.get(userId);
        }

        public Channel getSpectatorChannel(Integer userId) {
            return spectators.get(userId);
        }

        public boolean isEmpty() {
            return players.isEmpty() && spectators.isEmpty();
        }

        public void updateLastActiveTime() {
            this.lastActiveTime = LocalDateTime.now();
        }

        // Getters and Setters
        public String getRoomId() {
            return roomId;
        }

        public void setRoomId(String roomId) {
            this.roomId = roomId;
        }

        public GameState getGameState() {
            return gameState;
        }

        public void setGameState(GameState gameState) {
            this.gameState = gameState;
        }

        public LocalDateTime getCreateTime() {
            return createTime;
        }

        public void setCreateTime(LocalDateTime createTime) {
            this.createTime = createTime;
        }

        public LocalDateTime getLastActiveTime() {
            return lastActiveTime;
        }

        public void setLastActiveTime(LocalDateTime lastActiveTime) {
            this.lastActiveTime = lastActiveTime;
        }
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        try {
            Message message = (Message) msg;
            log.debug("收到消息: type={}, roomId={}, fromUserId={}", 
                message.getType(), message.getRoomId(), message.getFromUserId());

            switch (message.getType()) {
                case GAME_STATE:
                    handleGameState(ctx, message);
                    break;
                case MOVE:
                    handleMove(ctx, message);
                    break;
                case CHAT:
                    handleChat(ctx, message);
                    break;
                case SPECTATE:
                    handleSpectate(ctx, message);
                    break;
                case MATCH_STATUS:
                    handleMatchStatus(ctx, message);
                    break;
                case HEARTBEAT:
                    handleHeartbeat(ctx, message);
                    break;
                default:
                    log.warn("未知的消息类型: {}", message.getType());
                    sendError(ctx, "未知的消息类型");
            }
        } catch (Exception e) {
            log.error("处理消息时发生错误", e);
            sendError(ctx, "服务器内部错误");
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        log.error("通道异常", cause);
        ctx.close();
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        log.info("客户端断开连接: {}", ctx.channel().remoteAddress());
        // 清理相关资源
        cleanupResources(ctx.channel());
    }

    // 发送错误消息
    private void sendError(ChannelHandlerContext ctx, String errorMessage) {
        Message errorMsg = new Message(MessageType.ERROR);
        errorMsg.setData(errorMessage);
        ctx.writeAndFlush(errorMsg);
    }

    // 清理资源
    private void cleanupResources(Channel channel) {
        // 从所有房间中移除该通道
        for (Room room : rooms.values()) {
            room.getPlayers().values().remove(channel);
            room.getSpectators().values().remove(channel);
        }
    }

    // 处理游戏状态
    private void handleGameState(ChannelHandlerContext ctx, Message message) {
        try {
            String gameId = message.getRoomId();
            GameState gameState = redisService.getGameState(gameId);
            if (gameState != null) {
                Room room = rooms.get(gameId);
                if (room != null) {
                    broadcast(room.getRoomChannels(), message);
                    log.debug("广播游戏状态: gameId={}", gameId);
                } else {
                    log.warn("房间不存在: gameId={}", gameId);
                    sendError(ctx, "房间不存在");
                }
            } else {
                log.warn("游戏状态不存在: gameId={}", gameId);
                sendError(ctx, "游戏状态不存在");
            }
        } catch (Exception e) {
            log.error("处理游戏状态时发生错误", e);
            sendError(ctx, "处理游戏状态失败");
        }
    }

    // 处理走棋操作
    private void handleMove(ChannelHandlerContext ctx, Message message) {
        try {
            if (validateMove(message)) {
                String gameId = message.getRoomId();
                GameState newState = updateGameState(gameId, message);
                if (newState != null) {
                    redisService.saveGameState(gameId, newState);
                    Move move = convertToMove(message);
                    mysqlService.saveMove(move);
                    
                    Room room = rooms.get(gameId);
                    if (room != null) {
                        broadcast(room.getRoomChannels(), message);
                        log.debug("广播走棋信息: gameId={}, move={}", gameId, move);
                    }
                }
            } else {
                log.warn("非法走棋: message={}", message);
                sendError(ctx, "非法走棋");
            }
        } catch (Exception e) {
            log.error("处理走棋操作时发生错误", e);
            sendError(ctx, "处理走棋操作失败");
        }
    }

    // 处理聊天消息
    private void handleChat(ChannelHandlerContext ctx, Message message) {
        try {
            String gameId = message.getRoomId();
            GameChatInfo chat = convertToChat(message);
            if (chat != null) {
                mysqlService.saveChat(chat);
                Room room = rooms.get(gameId);
                if (room != null) {
                    broadcast(room.getRoomChannels(), message);
                    log.debug("广播聊天消息: gameId={}, chat={}", gameId, chat);
                }
            } else {
                log.warn("无效的聊天消息: message={}", message);
                sendError(ctx, "无效的聊天消息");
            }
        } catch (Exception e) {
            log.error("处理聊天消息时发生错误", e);
            sendError(ctx, "处理聊天消息失败");
        }
    }

    // 处理观战请求
    private void handleSpectate(ChannelHandlerContext ctx, Message message) {
        try {
            String gameId = message.getRoomId();
            Integer spectatorId = message.getFromUserId();
            
            redisService.addSpectator(gameId, spectatorId);
            
            Spectator spectator = new Spectator();
            spectator.setGameId(Integer.parseInt(gameId));
            spectator.setSpectatorId(spectatorId);
            spectator.setJoinedAt(LocalDateTime.now());
            mysqlService.saveSpectator(spectator);
            
            GameState gameState = redisService.getGameState(gameId);
            if (gameState != null) {
                ctx.writeAndFlush(new Message(MessageType.GAME_STATE, gameState));
                log.debug("发送游戏状态给观战者: gameId={}, spectatorId={}", gameId, spectatorId);
            }
        } catch (Exception e) {
            log.error("处理观战请求时发生错误", e);
            sendError(ctx, "处理观战请求失败");
        }
    }

    // 处理匹配状态
    private void handleMatchStatus(ChannelHandlerContext ctx, Message message) {
        try {
            Integer userId = message.getFromUserId();
            String matchMode = (String) message.getData();
            
            MatchQueueItem item = new MatchQueueItem(userId, matchMode);
            redisService.addToMatchQueue(matchMode, item);
            
            UserSession session = redisService.getUserSession(userId);
            if (session != null) {
                session.setMatchStatus(matchMode);
                redisService.saveUserSession(userId, session);
                log.debug("更新用户匹配状态: userId={}, matchMode={}", userId, matchMode);
            }
        } catch (Exception e) {
            log.error("处理匹配状态时发生错误", e);
            sendError(ctx, "处理匹配状态失败");
        }
    }

    // 处理心跳
    private void handleHeartbeat(ChannelHandlerContext ctx, Message message) {
        try {
            Integer userId = message.getFromUserId();
            redisService.updateUserOnlineStatus(userId, true);
            
            UserSession session = redisService.getUserSession(userId);
            if (session != null) {
                session.setLastHeartbeat(System.currentTimeMillis());
                redisService.saveUserSession(userId, session);
                log.debug("更新用户心跳: userId={}", userId);
            }
            
            ctx.writeAndFlush(new Message(MessageType.HEARTBEAT));
        } catch (Exception e) {
            log.error("处理心跳时发生错误", e);
            sendError(ctx, "处理心跳失败");
        }
    }

    // 广播消息
    private void broadcast(List<Channel> channels, Message message) {
        for (Channel channel : channels) {
            if (channel.isActive()) {
                channel.writeAndFlush(message);
            }
        }
    }

    // 验证走棋是否合法
    private boolean validateMove(Message message) {
        String gameId = message.getRoomId();
        GameState gameState = redisService.getGameState(gameId);
        if (gameState == null) {
            return false;
        }

        // 从消息中提取走棋信息
        Move move = convertToMove(message);
        if (move == null) {
            return false;
        }

        // 使用MoveValidator验证走棋是否合法
        return moveValidator.validateMove(gameState, move);
    }

    // 更新游戏状态
    private GameState updateGameState(String gameId, Message message) {
        GameState currentState = redisService.getGameState(gameId);
        if (currentState == null) {
            return null;
        }

        Move move = convertToMove(message);
        if (move == null) {
            return currentState;
        }

        // 创建新的游戏状态
        GameState newState = new GameState();
        newState.setGameId(currentState.getGameId());
        newState.setCurrentTurn(currentState.getCurrentTurn() == 1 ? 2 : 1);
        newState.setStatus(currentState.getStatus());
        newState.setPlayer1Id(currentState.getPlayer1Id());
        newState.setPlayer2Id(currentState.getPlayer2Id());
        newState.setLastMoveTime(System.currentTimeMillis());

        // 更新棋盘状态
        String boardState = currentState.getBoardState();
        StringBuilder newBoardState = new StringBuilder(boardState);
        
        // 计算起始和目标位置的索引
        int fromIndex = move.getFromY() * 9 + move.getFromX();
        int toIndex = move.getToY() * 9 + move.getToX();
        
        // 移动棋子
        newBoardState.setCharAt(toIndex, boardState.charAt(fromIndex));
        newBoardState.setCharAt(fromIndex, '0');
        
        // 如果吃子，更新被吃掉的棋子位置
        if (move.getIsCapture()) {
            newBoardState.setCharAt(toIndex, move.getPiece().charAt(0));
        }
        
        newState.setBoardState(newBoardState.toString());

        // 检查游戏是否结束
        if (isGameOver(newState)) {
            newState.setStatus("已结束");
            // 更新胜者
            newState.setWinnerId(move.getPlayerId());
        }

        return newState;
    }

    // 检查游戏是否结束
    private boolean isGameOver(GameState state) {
        String boardState = state.getBoardState();
        
        // 检查红方将是否被吃掉
        boolean redKingExists = false;
        // 检查黑方将是否被吃掉
        boolean blackKingExists = false;
        
        for (int i = 0; i < boardState.length(); i++) {
            char piece = boardState.charAt(i);
            if (piece == 'K') {
                redKingExists = true;
            } else if (piece == 'k') {
                blackKingExists = true;
            }
        }
        
        // 如果任一方将不存在，游戏结束
        return !redKingExists || !blackKingExists;
    }

    // 转换消息为走棋记录
    private Move convertToMove(Message message) {
        try {
            if (message.getType() != MessageType.MOVE) {
                return null;
            }

            Map<String, Object> data = (Map<String, Object>) message.getData();
            Move move = new Move();
            move.setGameId(Integer.parseInt(message.getRoomId()));
            move.setPlayerId(message.getFromUserId());
            move.setFromX((Integer) data.get("fromX"));
            move.setFromY((Integer) data.get("fromY"));
            move.setToX((Integer) data.get("toX"));
            move.setToY((Integer) data.get("toY"));
            move.setPiece((String) data.get("piece"));
            move.setMoveTime(LocalDateTime.now());
            move.setTimestamp(LocalDateTime.now());
            move.setMoveStr(String.format("%d,%d-%d,%d", 
                move.getFromX(), move.getFromY(), 
                move.getToX(), move.getToY()));
            move.setMoveNotation(generateMoveNotation(move));
            move.setComment((String) data.get("comment"));

            return move;
        } catch (Exception e) {
            log.error("转换走棋消息失败", e);
            return null;
        }
    }

    // 转换消息为聊天记录
    private GameChatInfo convertToChat(Message message) {
        if (message.getType() != MessageType.CHAT || message.getData() == null) {
            return null;
        }

        try {
            // 从消息数据中提取聊天信息
            Map<String, Object> chatData = (Map<String, Object>) message.getData();
            GameChatInfo chat = new GameChatInfo();
            
            // 设置基本信息
            chat.setGameId(Integer.parseInt(message.getRoomId()));
            chat.setSenderId(message.getFromUserId());
            chat.setChatTime(LocalDateTime.now());
            
            // 设置聊天内容
            chat.setMessage((String) chatData.get("content"));
            
            // 设置聊天类型（系统消息、玩家消息等）
            String chatType = (String) chatData.get("type");
            chat.setType(chatType != null ? chatType : "玩家消息");
            
            // 设置接收者ID（如果是私聊）
            Integer toUserId = (Integer) chatData.get("toUserId");
            if (toUserId != null) {
                chat.setToUserId(toUserId);
            }
            
            return chat;
        } catch (Exception e) {
            // 如果转换过程中出现任何错误，返回null
            return null;
        }
    }

    /**
     * 生成走棋记录
     */
    private String generateMoveNotation(Move move) {
        StringBuilder notation = new StringBuilder();
        
        // 添加棋子类型
        notation.append(move.getPiece());
        
        // 添加起始位置
        notation.append(String.format("%d%d", move.getFromX(), move.getFromY()));
        
        // 添加移动符号
        notation.append(move.getIsCapture() ? "x" : "-");
        
        // 添加目标位置
        notation.append(String.format("%d%d", move.getToX(), move.getToY()));
        
        return notation.toString();
    }
} 